
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanacclib.frontend.korona;

import ast.*;
import java.util.Stack;
import java.util.LinkedList;
import java.io.File;

import org.managarm.jmanacclib.core.CclibReader;
import org.managarm.jmanacclib.core.CclibUnit;
import org.managarm.jmanacclib.core.CclibException;

public class KorParser {
	// our environment object
	private final KorEnvironment environment;
	// scanner object we get our data from
	private final KorStream stream;
	private boolean strictMode;
	
	// constructs a parser
	public KorParser(final CclibReader reader,
			final KorEnvironment environment) throws CclibException {
		this.environment = environment;
		
		// create a scanner object for this parser
		final KorScanner scanner = new KorScanner(reader, environment);
		stream = new KorStream(scanner);
		// consume the first token
		stream.forceToken(KorToken.TYPE_CONTROL, "FirstToken");
	}
	
	public void parse(AstRoot root) throws CclibException {
		while(stream.endReached() == false) {
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER,
					"__parser_strict")) {
				stream.consume();
				strictMode = true;
			}
			parseRootElement(root, new LinkedList<AstTerm>());
		}
	}

	@SuppressWarnings("unchecked")
	public void parseRootElement(final AstRoot root,
			final LinkedList<AstTerm> imports) throws CclibException {
		final CclibUnit unit = stream.getLastUnit();
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "using")) {
			stream.consume();
			
			final AstTerm term = (AstTerm)parseSymbol();
			// clone the imports list and add this directive to it
			final LinkedList<AstTerm> subimports
					= (LinkedList<AstTerm>)imports.clone();
			subimports.add(term);
			
			// check for file-wide using directives
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ";")) {
				stream.consume();
				
				while(stream.endReached() == false) {
					parseRootElement(root, subimports);
				}
			}else{
				stream.forceToken(KorToken.TYPE_SPECIAL, "{");
				while(stream.tokenEquals(KorToken.TYPE_SPECIAL, "}")
						== false) {
					parseRootElement(root, subimports);
				}
				stream.forceToken(KorToken.TYPE_SPECIAL, "{");
			}
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "include")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			final String filename
					= stream.forceType(KorToken.TYPE_CONST_STRING);
						
			if(stream.endReached() == false
					&& stream.tokenEquals(KorToken.TYPE_SPECIAL, ";"))
				stream.consume();
			else
				System.out.println("Obsolete include directive in file "
						+ unit.unitPath);

			environment.parseFile(root, environment.findUnit
					(unit, line, filename));
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "import")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			final String library = stream.forceType
					(KorToken.TYPE_CONST_STRING);
			stream.forceToken(KorToken.TYPE_SPECIAL, ",");
			final String filename = stream.forceType
			(KorToken.TYPE_CONST_STRING);
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			
			final String libdir = environment.libraries.get(library);
			if(libdir == null)
				throw new CclibException("Unknown library " + library,
						unit, line);
			
			environment.parseFile(root, new CclibUnit(unit,
					line, libdir + File.separator + filename));
		}else{
			// parse classes and namespaces
			parseNamespaceElement(root, imports);
		}
	}
	
	public void parseNamespaceElement(final AstSpace space,
			final LinkedList<AstTerm> imports) throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "defmethod")){
			Integer mthcnt = space.getAttrInt("NumberOfMethods");
			if(mthcnt == null)
				mthcnt = new Integer(0);
			space.setAttribute("Method" + mthcnt, xparseMethod(false));
			mthcnt++;
			space.setAttribute("NumberOfMethods", mthcnt);
			return;
		}
		
		boolean is_generic = false;
		@SuppressWarnings("unused")
		boolean is_unmanaged = false;
		boolean is_struct = false;
		boolean is_static = false;
		boolean is_final = false;
		
		while(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "generic")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "unmanaged")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "struct")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "static")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "final")) {		
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "generic")) {
				stream.consume();
				is_generic = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "unmanaged")) {
				stream.consume();
				is_unmanaged = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "struct")) {
				stream.consume();
				is_struct = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "static")) {
				stream.consume();
				is_static = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "final")) {
				stream.consume();
				is_final = true;
			}
		}
		
		// check whether this is a class or namespace
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "namespace")) {
			stream.consume();
			
			// get the identifier
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			String path;
			
			if(space.getPath() == null) {
				path = ident;
			}else{
				path = space.getPath();
			}
			
			// resolve the namespace or create a new one
			AstSpace namespace = (AstSpace)space.getSpaceEntry(ident);
			if(namespace == null) {
				// create a new AstNamespace
				namespace = new AstSpace(ident, path, stream.getLastLine());
				
				// add it to the space
				space.addChild(ident, namespace);
			}
			
			AstSpace subspace = namespace;
			
			// check whether there are subspaces
			while(stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
				stream.consume();
				
				// fetch the subspace name
				String subident = stream.forceType(KorToken.TYPE_IDENTIFIER);
				
				// resolve namespaces
				AstSpace innerspace = (AstSpace)subspace.getSpaceEntry(subident);
				if(innerspace == null) {
					// resolve namespace path
					String subpath = subspace.getPath() + "." + subident;
					
					// create a new AstNamespace
					innerspace = new AstSpace(subident, subpath, stream.getLastLine());

					// add it to the parent namespace
					subspace.addChild(subident, innerspace);
				}
				
				subspace = innerspace;
			}
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ";")) {
				stream.consume();
				while(stream.endReached() == false)
					parseNamespaceElement(subspace, imports);
			}else{
				// consume the { token
				stream.forceToken(KorToken.TYPE_SPECIAL, "{");
				
				// parse the classes and inner namespaces
				while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, "}")) {
					parseNamespaceElement(subspace, imports);
				}
				
				// consume the } token
				stream.consume();
			}
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "class")) {
			stream.consume();
			
			// get the first part of the classname
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			// create a new AstNode
			AstGeneric generic = null;
			
			if(is_generic) {
				// consume the [ token
				stream.forceToken(KorToken.TYPE_SPECIAL, "[");
				
				// create a new AstGeneric
				generic = new AstGeneric(stream.getLastLine());
				
				// consume the generic type vars
				while(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false){
					// fetch the typevar name
					String typevar = stream.forceType(KorToken.TYPE_IDENTIFIER);
					
					// add it to the generic
					generic.addVariable(typevar);
					
					// consume the , token or break
					if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false)
						stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				}
				
				// consume the ] token
				stream.consume();
			}

			
			// determine the identifier/path of the class
			String path = ident;
			if(space.getPath() != null) {
				path = space.getPath() + '.' + ident;
			}
			
			AstTerm parentcls;
			if(path.equals("Korona.Object")) {
				parentcls = null;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "extends")) {
				stream.consume();
				parentcls = (AstTerm)parseFieldOrTypeOrValue();
			}else{
				AstTerm spaceterm = new AstTerm(stream.getLastLine(), "Korona");
				spaceterm.assignChild(new AstTerm(stream.getLastLine(), "Object"));
				parentcls = spaceterm;
			}
			
			// process the flags
			int mods = 0;
			if(is_struct)
				mods |= AstClass.MOD_STRUCT;
			if(is_static)
				mods |= AstClass.MOD_STATIC;
			if(is_final)
				mods |= AstClass.MOD_FINAL;
			
			// create a new ast class node
			final AstClass cls = new AstClass(ident, mods, path, space,
					parentcls, imports, stream.getLastLine());
			final CclibUnit unit = stream.getLastUnit();
			cls.setAttribute("unit", unit);
			
			stream.forceToken(KorToken.TYPE_SPECIAL, "{");
			
			// parse the methods and fields
			int methodcnt = 0;
			int fieldcnt = 0;
			while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, "}")) {
				final KorAstNode node = parseMember();
				if(node.getAttrStr("Node").equals("Method")) {
					cls.setAttribute("Method" + methodcnt, node);
					methodcnt++;
				}else if(node.getAttrStr("Node").equals("Field")) {
					cls.setAttribute("Field" + fieldcnt, node);
					fieldcnt++;
				}else throw new IllegalStateException();
			}
			cls.setAttribute("NumberOfMethods", new Integer(methodcnt));
			cls.setAttribute("NumberOfFields", new Integer(fieldcnt));
			
			// consume the } token
			stream.consume();
			
			// add it to the parent node
			if(generic != null) {
				// assign the class to the generic
				generic.assignBody(cls);
				
				// return the generic
				space.addChild(ident, generic);
			}else{
				// return the class
				space.addChild(ident, cls);
			}
		}else{
			// this is no valid namespace or class
			throw stream.tokenError("class or namespace");
		}
	}
	
	private KorAstNode parseMember() throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "decmethod")) {
			return xparseMethod(true);
		}
		boolean is_native = false;
		boolean is_static = false;
		boolean is_abstract = false;
		boolean is_const = false;
		boolean is_constructor = false;
		boolean is_final = false;
		
		while(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "native")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "abstract")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "final")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "static")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "public")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "private")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "protected")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "constructor")
				|| stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "destructor")) {
		
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "native")) {
				stream.consume();
				is_native = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "abstract")) {
				stream.consume();
				is_abstract = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "final")) {
				stream.consume();
				is_final = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "static")) {
				stream.consume();
				is_static = true;
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "public")) {
				stream.consume();
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "private")) {
				stream.consume();
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "protected")) {
				stream.consume();
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "constructor")) {
				stream.consume();
				is_constructor = true;
			}
		}
		
		KorAstNode type;
		if(is_constructor == false) {
			type = parseFieldOrTypeOrValue();
		}else{
			type = null;
		}
		
		// if the method/field is no constructor, parse the identifier
		String ident;
		if(is_constructor == false || stream.tokenEquals(KorToken.TYPE_SPECIAL, "(") == false) {
			ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
		}else{
			ident = "constructor";
		}
		
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
			// consume the ( token
			stream.consume();
			
			KorAstNode method = new KorAstNode();
			method.setAttribute("Node", "Method");
			
			// process the identifier & return type
			method.setAttribute("MethodName", ident);
			if(is_constructor == false)
				method.setAttribute("ReturnType", type);
			
			// process the method flags
			if(is_static)
				method.setAttribute("FlagStatic", new Boolean(true));
			if(is_native)
				method.setAttribute("FlagNative", new Boolean(true));
			if(is_constructor) //FIXME: do we need this?
				method.setAttribute("FlagConstructor", new Boolean(true));
			if(is_abstract)
				method.setAttribute("FlagAbstract", new Boolean(true));
			if(is_final)
				method.setAttribute("FlagFinal", new Boolean(true));
			
			// parse the arguments
			int argcnt = 0;
			while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")")) {
				// parse the argument type and identifier
				KorAstNode argtype = parseFieldOrTypeOrValue();
				String argident = stream.forceType(KorToken.TYPE_IDENTIFIER);
				
				// add it to the method
				method.setAttribute("ArgumentName" + argcnt, argident);
				method.setAttribute("ArgumentType" + argcnt, argtype);
				argcnt++;
				
				// check for comma or right round bracket
				if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ",")) {
					stream.consume();
				}else{
					stream.assertToken(KorToken.TYPE_SPECIAL, ")");
				}
			}
			// consume the right round bracket
			stream.consume();
			method.setAttribute("NumberOfArguments", new Integer(argcnt));
			
			// parse the method body
			if(is_native == true || is_abstract == true) {
				stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			}else{
				method.setAttribute("Statement", parseStatement(true));
			}
			return method;
		}else{
			int flags = AstField.FLAG_NONE;
			if(is_static)
				flags |= AstField.FLAG_STATIC;
			if(is_const)
				flags |= AstField.FLAG_CONST;
			
			AstField field = new AstField(stream.getLastLine(), type, ident, flags);
			field.setAttribute("Node", "Field");
			
			// parse inititial values
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "=")) {
				stream.consume();
				field.assignExpression(parseExpression());
			}
			stream.forceToken(KorToken.TYPE_SPECIAL, ";");

			return field;
		}
	}
	
	private KorAstNode parseStatement(boolean standalone) throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ";")) {
			// process lonely ; tokens
			stream.consume();
			
			// return a new AstNoAction node
			return new AstNoAction(stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "def")) {
			stream.consume();
			
			// parse the variable data type
		//	scanner.forceToken(CmpToken.TOKEN_SPECIAL, "[");
			final KorAstNode typenode = parseFieldOrTypeOrValue();
		//	scanner.forceToken(CmpToken.TOKEN_SPECIAL, "]");
			// parse the variable identifier
			final String ident = stream.getCurrentString();
			stream.consume();
			
			// parse the initial value
			KorAstNode exp = null;
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "=")) {
				stream.consume();
				exp = parseExpression();
			}
			
			final AstVariable variable = new AstVariable(ident,
					typenode, exp, stream.getLastLine());
			
			// parse the ; token
			if(standalone)
				stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			
			return variable;
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "{")) {
			// consume the { token
			stream.consume();
			
			// create a new container
			AstBlock container = new AstBlock(stream.getLastLine());
			
			while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, "}")) {
				// parse the statement
				KorAstNode node = parseStatement(true);
				
				// add it to the container
				container.addChild(node);
			}
			// consume the } token
			stream.consume();
			
			// return the container
			return container;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "return")) {
			stream.consume();
			
			KorAstNode exp = null;
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ";") == false) {
				exp = parseExpression();
			}
			
			// create the astnode
			AstReturn ret = new AstReturn(exp, stream.getLastLine());
			
			// parse the ; token
			if(standalone)
				stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			
			return ret;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "if")) {
			// consume the if ( tokens
			stream.consume();
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			
			// parse the expression
			KorAstNode exp = parseExpression();
			
			// consume the ) token
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			
			// parse the statement
			KorAstNode casestat = parseStatement(false);
			
			// else statement
			KorAstNode elsestat = null;
			
			// parse the else statement
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "else")) {
				// consume the else token
				stream.consume();
				
				// parse the else statement
				elsestat = parseStatement(true);
			}
			
			return new AstCondIf(exp, casestat, elsestat, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "while")) {
			// consume the while ( tokens
			stream.consume();
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			
			// parse the expression
			KorAstNode exp = parseExpression();

			// consume the ) token
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			
			// parse the statement
			KorAstNode statement = parseStatement(true);
			
			// return a new AstCondWhile node
			return new AstCondWhile(exp, statement, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "do")) {
			stream.consume();

			KorAstNode statement = parseStatement(false);
			stream.forceToken(KorToken.TYPE_IDENTIFIER, "until");
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			KorAstNode exp = parseExpression();
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			
			return new AstCondDo(statement, exp, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "for")) {
			stream.consume();
			
			
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			KorAstNode initstat = parseStatement(false);
			stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			KorAstNode exp = parseExpression();
			stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			KorAstNode loopstat = parseStatement(false);
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			
			final KorAstNode blockstat = parseStatement(true);
			
			// each for statement gets an own block
			// this makes for-statements that declare variables possible
			final AstBlock container = new AstBlock(stream.getLastLine());
			container.addChild(initstat);
			container.addChild(new AstCondFor(exp,
					loopstat, blockstat, stream.getLastLine()));
			return container;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "try")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			// parse the try-block
			final KorAstNode try_block = new KorAstNode(line);
			try_block.setAttribute("Name", "TryCatch");
			try_block.setAttribute("Statement", parseStatement(true));
			
			// parse catch-blocks (there can be multiple catch-blocks)
			int catchnum = 0;
			while(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "catch")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode catch_type = xparseDatatype();
				final String catch_name =
					stream.forceType(KorToken.TYPE_IDENTIFIER);
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				try_block.setAttribute("CatchType" + catchnum,
						catch_type);
				try_block.setAttribute("CatchVariable" + catchnum,
						catch_name);
				try_block.setAttribute("CatchStatement" + catchnum,
						parseStatement(true));
				catchnum++;
			}
			try_block.setAttribute("NumberOfCatches", new Integer(catchnum));
			
			return try_block;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "throw")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			final KorAstNode statement = new KorAstNode(line);
			statement.setAttribute("Name", "Throw");
			statement.setAttribute("Exception", parseFieldOrTypeOrValue());
			
			// parse the ; token
			if(standalone)
				stream.forceToken(KorToken.TYPE_SPECIAL, ";");
			
			return statement;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_nat")) {
			final CclibUnit unit = stream.getLastUnit();
			final int line = stream.getLastLine();
			stream.consume();
			stream.forceToken(KorToken.TYPE_SPECIAL, ":");
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "refinc")) {
				stream.consume();
					
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode operand = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
					
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				return new AstXNatRefOperation
						(AstXNatRefOperation.REF_INC, operand, line);
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "refdec")) {
				stream.consume();
					
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode operand = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
					
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				return new AstXNatRefOperation
						(AstXNatRefOperation.REF_DEC, operand, line);
			}else{
				throw new CclibException("Parse error: Illegal extension",
						unit, line);
			}
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_perf")) {
			final CclibUnit unit = stream.getLastUnit();
			final int line = stream.getLastLine();
			stream.consume();
			stream.forceToken(KorToken.TYPE_SPECIAL, ":");
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "arraycopy")) {
				stream.consume();
					
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode dest = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode source = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode destoff = parseExpression();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode srcoff = parseExpression();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode size = parseExpression();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
					
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				return new AstXPerfArrayCopy(dest, source, destoff, srcoff, size, line);
			}else{
				throw new CclibException("Parse error: Invalid extension",
						unit, line);
			}
		}else{
			final KorAstNode node = parseFieldOrTypeOrValue();
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "+=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_ADD_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "-=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_SUB_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "*=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_MUL_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "/=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_DIV_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "|=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_OR_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "&=")) {
				// consume the operator token and parse the expression
				stream.consume();
				KorAstNode exp = parseExpression();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstAssignment node and return it
				return new AstAssignment(stream.getLastLine(),
						AstAssignment.OP_AND_ASSIGN, node, exp);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "++")) {
				// consume the operator token
				stream.consume();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstIncrement node
				return new AstIncrement(stream.getLastLine(),
						AstIncrement.OP_INCREMENT, node);
			}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "--")) {
				// consume the operator token
				stream.consume();
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// create an AstIncrement node
				return new AstIncrement(stream.getLastLine(),
						AstIncrement.OP_DECREMENT, node);
			}else if(isStatement(node)) {
				// consume the ;
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				// return the node
				return node;
			}
			
			if(stream.typeEquals(KorToken.TYPE_IDENTIFIER)) {
				// it's a variable declaration
				// get the variable name and consume it
				String ident = stream.getCurrentString();
				stream.consume();
				
				// the init value if this variable
				KorAstNode exp = null;
				
				// check whether it has an init value
				if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "=")) {
					stream.consume();
					
					// parse the init value
					exp = parseExpression();
				}
				
				// create a new AstVariable object
				AstVariable var = new AstVariable(ident, node, exp, stream.getLastLine());
				if(strictMode)
					throw new CclibException("Strict mode: Illegal variable declaration",
							stream.getLastUnit(), stream.getLastLine());
				
				// parse the ; token
				if(standalone)
					stream.forceToken(KorToken.TYPE_SPECIAL, ";");
				
				return var;
			}

			final CclibUnit unit = stream.getLastUnit();
			throw new CclibException("Parse error: Could not " +
					"parse statement: Unexpected " + stream.getCurrentString(),
					unit, stream.getLastLine());
		}
	}
	
	private boolean isStatement(KorAstNode node) {
		// check for blocks
		if(node instanceof AstBlock)
			return true;
		
		if(node instanceof AstSymbol == false)
			return false;
		
		AstSymbol symbol = (AstSymbol)node;
		
		while(symbol != null) {
			if(symbol instanceof AstCall) {
				return true;
			}else if(symbol instanceof AstXAtomCas) {
				return true;
			}
			
			symbol = symbol.getChild();
		}
		
		return false;
	}
	
	private KorAstNode parseExpression() throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
			stream.consume();
			KorAstNode node = parseExpression();
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			// an operator might follow the operand
			if(parseOperator() != -1) {
				return parseOperation(node);
			}else{
				return node;
			}
		}else{
			KorAstNode op = parseFieldOrTypeOrValue();
			// an operator might follow the operand
			if(parseOperator() != -1) {
				return parseOperation(op);
			}else{
				return op;
			}
		}
	}	
	
	private KorAstNode parseSymbol() throws CclibException {
		AstSymbol symbol = null;
		AstSymbol subsymbol = null;
		
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "$")) {
			stream.consume();
			
			int line = stream.getLastLine();
			
			// fetch the identifier
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			stream.forceToken(KorToken.TYPE_SPECIAL, "[");
			
			// create a new AstInstance
			AstInstance instance = new AstInstance(stream.getLastLine());
			
			while(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false){
				KorAstNode type = parseFieldOrTypeOrValue();
				
				// add the type variable
				instance.addVariable(type);
					
				// consume the , token or break
			
				if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false)
					stream.forceToken(KorToken.TYPE_SPECIAL, ",");
			}
			
			// consume the ] token
			stream.consume();
			
			// return the instance
			symbol = new AstTerm(line, ident);
			symbol.assignChild(instance);
			subsymbol = instance;
		}else{
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			symbol = new AstTerm(stream.getLastLine(), ident);
			subsymbol = symbol;
		}
		
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "[")
				|| stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")
				|| stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
				// consume the token
				stream.consume();
			}
			
			// parse sub symbols
			KorAstNode node = parseSubSymbol(subsymbol);
			if(node instanceof AstSymbol) {
				// add the symbol to the path
				subsymbol.assignChild((AstSymbol)node);
			}else{
				// return the node
				return node;
			}
		}
		
		// return the symbol/path
		return symbol;
	}
	
	private KorAstNode parseSubSymbol(AstSymbol parent) throws CclibException {
		AstSymbol symbol = null;
		AstSymbol subsymbol = null;
		
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
			// it's a method call
			// consume the ( token
			stream.consume();
			
			// create an AstCall node
			AstCall callnode = new AstCall(stream.getLastLine());
			
			while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")")) {
				// add the parameter
				callnode.addParameter(parseExpression());
				
				// consume the , token or break
				if(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")")) {
					stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				}
			}
			
			// consume the )
			stream.consume();
			
			// return the call node
			symbol = callnode;
			subsymbol = symbol;
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "[")) {
			// process array access
			// consume the [ token
			stream.consume();
			
			// distinguish between array access and array types
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]")) {
				stream.consume();
				
				symbol = new  AstArray(stream.getLastLine());
				subsymbol = symbol;
				if(strictMode)
					throw new CclibException("Strict mode: Illegal use of []",
							stream.getLastUnit(), stream.getLastLine());
			}else{
				// parse the index expression
				KorAstNode index = parseExpression();
			
				// consume the ] token
				stream.forceToken(KorToken.TYPE_SPECIAL, "]");
			
				// return the array access
				symbol = new AstArrayAccess(stream.getLastLine(), index, 1);
				subsymbol = symbol;
			}
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "$")) {
			// consume the $ token
			stream.consume();
			
			int line = stream.getLastLine();
			
			// fetch the identifier
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			// consume the [ token
			stream.forceToken(KorToken.TYPE_SPECIAL, "[");
			
			// create a new AstInstance
			AstInstance instance = new AstInstance(stream.getLastLine());
			
			//
			while(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false){
				KorAstNode type = parseFieldOrTypeOrValue();
				
				// add the type variable
				instance.addVariable(type);
					
				// consume the , token or break
			
				if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "]") == false)
					stream.forceToken(KorToken.TYPE_SPECIAL, ",");
			}
			
			// consume the ] token
			stream.consume();
			
			// return the instance
			symbol = new AstTerm(line, ident);
			symbol.assignChild(instance);
			subsymbol = instance;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_atom")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			stream.forceToken(KorToken.TYPE_SPECIAL, ":");
			
			// atomic operation:
			// compare the field and the first (value) parameter
			// if they are equal load the second (source) parameter
			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "cas")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode value = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode source = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				symbol = new AstXAtomCas(source, value, line);
				subsymbol = symbol;
			}else{
				final CclibUnit unit = stream.getLastUnit();
				throw new CclibException("Parse error: Illegal extension",
						unit, line);
			}
		}else{
			String ident = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			symbol = new AstTerm(stream.getLastLine(), ident);
			subsymbol = symbol;
		}
		
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "[")
				|| stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")
				|| stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
				// consume the token
				stream.consume();
			}
			
			// parse sub symbols
			KorAstNode node = parseSubSymbol(parent);
			if(node instanceof AstSymbol) {
				// add the symbol to the path
				subsymbol.assignChild((AstSymbol)node);
			}else{
				// return the node
				return node;
			}
		}
		
		// return the symbol
		return symbol;
	}
	
	private KorAstNode parseFieldOrTypeOrValue() throws CclibException {
		if(stream.typeEquals(KorToken.TYPE_CONST_CHAR)) {
			// store the constant
			char cnst = stream.getCurrentString().charAt(0);
			
			// consume the charconst
			stream.consume();
			
			// return the new token
			return new AstConstChar(stream.getLastLine(), cnst);
		}else if(stream.typeEquals(KorToken.TYPE_CONST_STRING)) {
			String cnst = stream.getCurrentString();
			stream.consume();
			
			while(stream.tokenEquals(KorToken.TYPE_SPECIAL, "+")) {
				stream.consume();
				cnst += stream.forceType(KorToken.TYPE_CONST_STRING);
			}
			
			// return the new token
			return new AstConstString(stream.getLastLine(), cnst);
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "null")) {
			stream.consume();
			
			// create a new AstNull node
			return new AstConstNull(stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "cast")) {
			stream.consume();
			
			// consume the [ token
			stream.forceToken(KorToken.TYPE_SPECIAL, "[");
			
			// parse the symbol
			KorAstNode symbol = xparseDatatype();
			
			// consume the ] token
			stream.forceToken(KorToken.TYPE_SPECIAL, "]");
			
			// parse the expression
			KorAstNode exp;
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
				exp = parseExpression();
			}else{
				exp = parseFieldOrTypeOrValue();
			}
			
			// create a new AstCast node
			AstCast cast = new AstCast(symbol, exp, stream.getLastLine());
			
			// return the cast node
			return cast;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "delegate")) {
			// consume "delegate"
			stream.consume();
			
			// parse the return type name
			String retname = stream.forceType(KorToken.TYPE_IDENTIFIER);
			
			// create a term for it
			AstTerm retterm = new AstTerm(stream.getLastLine(), retname);
			
			// create a delegate node
			AstDelegate delegate = new AstDelegate(retterm, stream.getLastLine());
			
			// consume "("
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			
			while(stream.tokenEquals(KorToken.TYPE_SPECIAL, ")") == false) {
				// fetch the type
				KorAstNode node = parseFieldOrTypeOrValue();
				
				// add it to the delegate
				delegate.addArgument(node);
				
				if(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")"))
					stream.forceToken(KorToken.TYPE_SPECIAL, ",");
			}
			
			// consume ")"
			stream.consume();
			
			return delegate;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "sizeof")) {
			stream.consume();
			
			// consume the ( token
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			
			// parse the symbol
			KorAstNode symbol = parseFieldOrTypeOrValue();
			
			// create a new AstSizeOf node
			AstSizeOf operator = new AstSizeOf(symbol, stream.getLastLine());
			
			// consume the ) token
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			
			// return the operator
			return operator;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "new")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
				stream.consume();
				final KorAstNode length = parseExpression();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				final KorAstNode typenode = parseFieldOrTypeOrValue();
				
				final KorAstNode node = new KorAstNode(line);
				node.setAttribute("Name", "NewArray");
				node.setAttribute("Length", length);
				node.setAttribute("Datatype", typenode);
				return node;
			}
			
			KorAstNode constructor = parseSymbol();
			
			return new AstNew(constructor, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "newarray")) {
			final int line = stream.getLastLine();
			stream.consume();
			
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			final KorAstNode length = parseExpression();
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			final KorAstNode typenode = parseFieldOrTypeOrValue();
			
			final KorAstNode node = new KorAstNode(line);
			node.setAttribute("Name", "NewArray");
			node.setAttribute("Length", length);
			node.setAttribute("Datatype", typenode);
			return node;
		/* ----- PARSE XNAT EXTENSION BUILTINS ----- */
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_nat")) {
			final int current_line = stream.getLastLine();
			stream.consume();
			stream.forceToken(KorToken.TYPE_SPECIAL, ":");

			if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "addresscast")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "[");
				final KorAstNode type = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, "]");
				final KorAstNode address;
				if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
					address = parseExpression();
				}else{
					address = parseFieldOrTypeOrValue();
				}
				
				return new AstXNatAddrCast(type, address, stream.getLastLine());
			}else	if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "addressof")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode symbol = parseSymbol();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				return new AstXNatAddressOf(symbol, current_line);
			}else	if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "objectsize")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "[");
				final KorAstNode symbol = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, "]");
				
				return new AstXNatObjSize(symbol, current_line);
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "dlg_method")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				KorAstNode symbol = parseSymbol();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				return new AstXNatDlgMethod(symbol, current_line);
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "dlg_object")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				KorAstNode symbol = parseSymbol();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				return new AstXNatDlgObject(symbol, current_line);
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "mthaddress")) {
				stream.consume();

				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode symbol = parseSymbol();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				return new AstXNatMthAddr(symbol, current_line);
			}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "mk_delegate")) {
				stream.consume();
				
				stream.forceToken(KorToken.TYPE_SPECIAL, "[");
				final KorAstNode type = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, "]");
				stream.forceToken(KorToken.TYPE_SPECIAL, "(");
				final KorAstNode method = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				final KorAstNode object = parseFieldOrTypeOrValue();
				stream.forceToken(KorToken.TYPE_SPECIAL, ")");
				
				return new AstXNatMakeDelegate(type, method, object, current_line);
			}else{
				throw new UnsupportedOperationException("Invalid xnat extension");
			}
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "delegateof")) {
			stream.consume();

			return new AstDelegateOf(parseSymbol(), stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "arrayof")) {
			stream.consume();
			
			final KorAstNode astnode = new KorAstNode(stream.getLastLine());
			astnode.setAttribute("Name", "ArrayOf");
			
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "[")) {
				stream.consume();
				astnode.setAttribute("Datatype", parseFieldOrTypeOrValue());
				stream.forceToken(KorToken.TYPE_SPECIAL, "]");
			}
			
			// parse the array elements
			int elementnum = 0;
			stream.forceToken(KorToken.TYPE_SPECIAL, "(");
			while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")")) {
				astnode.setAttribute("Element" + elementnum,
						parseFieldOrTypeOrValue());
				
				if(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")"))
					stream.forceToken(KorToken.TYPE_SPECIAL, ",");
				elementnum++;
			}
			stream.consume();
			astnode.setAttribute("NumberOfElements", new Integer(elementnum));

			return astnode;
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "-")) {
			int line = stream.getLastLine();
			stream.consume();
			final KorAstNode operand = parseOperand();
			
			final KorAstNode node = new KorAstNode(line);
			node.setAttribute("Name", "NegateNumber");
			node.setAttribute("Operand", operand);
			
			return node;
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "!")) {
			int line = stream.getLastLine();
			stream.consume();
			final KorAstNode operand = parseOperand();
			
			final KorAstNode node = new KorAstNode(line);
			node.setAttribute("Name", "NegateBits");
			node.setAttribute("Operand", operand);
			
			return node;
		}else if(stream.typeEquals(KorToken.TYPE_CONST_NUMERIC)) {
			// get the integer value
			long cnst = Long.parseLong(stream.getCurrentString());
			// consume the token and return an AstConstInteger node
			stream.consume();
			return new AstConstInteger(stream.getLastLine(), cnst);
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "true")) {
			int line = stream.getLastLine();
			stream.consume();
			return new AstConstBoolean(true, line);
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "false")) {
			int line = stream.getLastLine();
			stream.consume();
			return new AstConstBoolean(false, line);
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_xnat_objsize")) {
			// consume the token
			stream.consume();
			
			// consume the [ token
			stream.forceToken(KorToken.TYPE_SPECIAL, "[");
			
			// parse the type symbol
			KorAstNode type = parseFieldOrTypeOrValue();
			
			// consume the ] token
			stream.forceToken(KorToken.TYPE_SPECIAL, "]");
			
			return new AstXNatObjSize(type, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "_xnat_addrcast")) {
			// consume the token
			stream.consume();
			
			// consume the [ token
			stream.forceToken(KorToken.TYPE_SPECIAL, "[");
			
			// parse the type symbol
			KorAstNode type = parseFieldOrTypeOrValue();
			
			// consume the ] token
			stream.forceToken(KorToken.TYPE_SPECIAL, "]");
			
			// parse the address symbol
			KorAstNode address = parseFieldOrTypeOrValue();
			
			return new AstXNatAddrCast(type, address, stream.getLastLine());
		}else if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "?")) {
			int line = stream.getLastLine();
			stream.consume();
			//scanner.forceToken(CmpToken.TOKEN_SPECIAL, "[");
			KorAstNode node = parseFieldOrTypeOrValue();
			//scanner.forceToken(CmpToken.TOKEN_SPECIAL, "]");
			return new AstArrayType(node, line);
		}
		
		// process type modifiers
		if(stream.typeEquals(KorToken.TYPE_IDENTIFIER)) {
			final int line = stream.getLastLine();
			
			// our flags
			int flags1 = 0;
			
			while(stream.stringEquals("invariant")
					|| stream.stringEquals("const")
					|| stream.stringEquals("unmanaged")
					|| stream.stringEquals("struct")
					|| stream.stringEquals("local")
					|| stream.stringEquals("_xunsafe")
					|| stream.stringEquals("_nat_t")) {
				if(stream.stringEquals("_nat_t")) {
					stream.consume();
					stream.forceToken(KorToken.TYPE_SPECIAL, ":");
					
					if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "refcounted")) {
						flags1 |= AstType.FLAG1_XNAT_REFCOUNTED;
					}else{
						throw new UnsupportedOperationException("Invalid xnat extension");
					}
					stream.consume();
				}else{
					// process the token
					if(stream.stringEquals("struct")) {
						//flags1 |= AstType.FLAG1_STRUCT;
					}else if(stream.stringEquals("local")) {
						flags1 |= AstType.FLAG1_LOCAL;
					}else if(stream.stringEquals("_xunsafe")) {
	//					flags1 |= AstType.FLAG1_XUNSAFE;
					}else if(stream.stringEquals("unmanaged")) {
						flags1 |= AstType.FLAG1_UNMANAGED;
					}
					stream.consume();
				}
			}
			
			if(flags1 != 0)
				return new AstType(flags1, parseFieldOrTypeOrValue(), line);
		}
		
		// consume the token
		return parseSymbol();
	}
	
	private KorAstNode parseOperation(final KorAstNode leftterm) throws CclibException {
		final int line = stream.getLastLine();
		
		// stores our operands and operators
		final Stack<Integer> operators = new Stack<Integer>();
		final Stack<KorAstNode> operands = new Stack<KorAstNode>();
		
		// setup the initial stack
		operands.push(leftterm);
		
		while(parseOperator() != -1) {
			// parse the operator and right operand
			final int operator = parseOperator();
			stream.consume(); // consume the operator
			final KorAstNode operand = parseOperand();
			// push them onto the operand stacks
			operators.push(operator);
			operands.push(operand);
		}
		
		// process the operations
		while(operators.size() != 0)
			operands.push(processOperation(line, operators, operands));
		
		final KorAstNode operation = operands.pop();
		if(operators.size() != 0) // do some sanity checks
			throw new IllegalStateException();
		if(operands.size() != 0)
			throw new IllegalStateException();
		return operation;
	}
	
	public KorAstNode processOperation(final int line,
			final Stack<Integer> operators,
			final Stack<KorAstNode> operands) {
		// get the operator and the right operand
		final int operator = operators.pop();
		final KorAstNode right = operands.pop();
		
		while(operators.size() > 0 
				&& getPrecedence(operators.peek()) >= getPrecedence(operator))
			operands.push(processOperation(line, operators, operands));
		
		// this operator gets the next operand
		final KorAstNode left = operands.pop();
		// create a new operation node
		final AstOperation operation = new AstOperation(line, operator);
		operation.assignOperand1(left);
		operation.assignOperand2(right);
		return operation;
	}
	
	private KorAstNode parseOperand() throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "(")) {
			stream.consume();
			final KorAstNode operand = parseExpression();
			stream.forceToken(KorToken.TYPE_SPECIAL, ")");
			return operand;
		}else{
			return parseFieldOrTypeOrValue();
		}
	}
	
	public int getPrecedence(final int operator) {
		if(operator == AstOperation.OP_ARITHMETIC_MUL
				|| operator == AstOperation.OP_ARITHMETIC_DIV
				|| operator == AstOperation.OP_ARITHMETIC_MOD
				|| operator == AstOperation.OP_BINARY_SHL
				|| operator == AstOperation.OP_BINARY_SHR)
			return 6;
		if(operator == AstOperation.OP_ARITHMETIC_ADD
				|| operator == AstOperation.OP_ARITHMETIC_SUB
				|| operator == AstOperation.OP_BINARY_OR
				|| operator == AstOperation.OP_BINARY_AND
				|| operator == AstOperation.OP_BINARY_XOR)
			return 5;
		if(operator == AstOperation.OP_SPECIAL_INSTANCEOF)
			return 4;
		if(operator == AstOperation.OP_COMPARE_EQUALS
				|| operator == AstOperation.OP_COMPARE_GREATER
				|| operator == AstOperation.OP_COMPARE_LESS
				|| operator == AstOperation.OP_COMPARE_NOT_EQUALS
				|| operator == AstOperation.OP_COMPARE_NOT_GREATER
				|| operator == AstOperation.OP_COMPARE_NOT_LESS)
			return 3;
		if(operator == AstOperation.OP_LOGICAL_OR
				|| operator == AstOperation.OP_LOGICAL_AND)
			return 2;
		return -1;
	}
	
	public int parseOperator() throws CclibException {
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "+"))
			return AstOperation.OP_ARITHMETIC_ADD;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "-"))
			return AstOperation.OP_ARITHMETIC_SUB;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "*"))
			return AstOperation.OP_ARITHMETIC_MUL;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "/"))
			return AstOperation.OP_ARITHMETIC_DIV;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "%"))
			return AstOperation.OP_ARITHMETIC_MOD;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "=="))
			return AstOperation.OP_COMPARE_EQUALS;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "!="))
			return AstOperation.OP_COMPARE_NOT_EQUALS;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ">"))
			return AstOperation.OP_COMPARE_GREATER;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "<"))
			return AstOperation.OP_COMPARE_LESS;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ">="))
			return AstOperation.OP_COMPARE_NOT_LESS;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "<="))
			return AstOperation.OP_COMPARE_NOT_GREATER;
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "instanceof"))
			return AstOperation.OP_SPECIAL_INSTANCEOF;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "&&"))
			return AstOperation.OP_LOGICAL_AND;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "||"))
			return AstOperation.OP_LOGICAL_OR;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "<<"))
			return AstOperation.OP_BINARY_SHL;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ">>"))
			return AstOperation.OP_BINARY_SHR;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "&"))
			return AstOperation.OP_BINARY_AND;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "|"))
			return AstOperation.OP_BINARY_OR;
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, "^"))
			return AstOperation.OP_BINARY_XOR;
		return -1;
	}
	
	public KorAstNode xparseDatatype() throws CclibException {
		return parseFieldOrTypeOrValue();
	}
	
	public KorAstNode xparseSymbol() throws CclibException {
		final KorAstNode symbol = new KorAstNode();
		// parse the identifier
		symbol.setAttribute("Identifier", stream.forceType
				(KorToken.TYPE_IDENTIFIER));
		// parse subsymbols
		if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ".")) {
			stream.consume();
			symbol.setAttribute("SubSymbol", xparseSymbol());
		}
		return symbol;
	}
	
	public KorAstNode xparseStatement() throws CclibException {
		return parseStatement(true);
	}
	
	public KorAstNode xparseMethod(final boolean inside_classdef) throws CclibException {
		// determine whether this is a declaration or a definition
		final boolean implementation;
		if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "decmethod")) {
			stream.consume();
			implementation = false;
		}else if(stream.tokenEquals(KorToken.TYPE_IDENTIFIER, "defmethod")) {
			stream.consume();
			implementation = true;
		}else{
			throw new IllegalStateException();
		}
		
		// create a new synatx tree node
		final KorAstNode mthnode = new KorAstNode();
		mthnode.setAttribute("Node", "Method");
		
		// parse the method flags
		while(stream.typeEquals(KorToken.TYPE_IDENTIFIER)
				&& (stream.stringEquals("final")
						|| stream.stringEquals("abstract")
						|| stream.stringEquals("static")
						|| stream.stringEquals("native")
						|| stream.stringEquals("public")
						|| stream.stringEquals("private")
						|| stream.stringEquals("protected"))) {
			if(stream.stringEquals("final")) {
				mthnode.setAttribute("FlagFinal", new Boolean(true));
			}else if(stream.stringEquals("static")) {
					mthnode.setAttribute("FlagStatic", new Boolean(true));
			}else if(stream.stringEquals("native")) {
				mthnode.setAttribute("FlagNative", new Boolean(true));
			}else if(stream.stringEquals("abstract")) {
				mthnode.setAttribute("FlagAbstract", new Boolean(true));
			}
			stream.consume();
		}
		
		// parse the method return type
		mthnode.setAttribute("ReturnType", xparseDatatype());
		// parse the class name
		if(inside_classdef == false) {
			mthnode.setAttribute("ClassName", xparseSymbol());
			stream.forceToken(KorToken.TYPE_SPECIAL, ":");
		}
		// parse the method name
		mthnode.setAttribute("MethodName",
				stream.forceType(KorToken.TYPE_IDENTIFIER));
		// parse the method arguments
		int index = 0;
		stream.forceToken(KorToken.TYPE_SPECIAL, "(");
		while(!stream.tokenEquals(KorToken.TYPE_SPECIAL, ")")) {
			mthnode.setAttribute("ArgumentType" + index, xparseDatatype());
			mthnode.setAttribute("ArgumentName" + index, stream.forceType
					(KorToken.TYPE_IDENTIFIER));
			index++;
			// check for comma or right round bracket
			if(stream.tokenEquals(KorToken.TYPE_SPECIAL, ",")) {
				stream.consume();
			}else{
				stream.assertToken(KorToken.TYPE_SPECIAL, ")");
			}
		}
		stream.consume();
		mthnode.setAttribute("NumberOfArguments", new Integer(index));

		// parse the method implementation
		if(implementation) {
			mthnode.setAttribute("Statement", xparseStatement());
		}else{
			stream.forceToken(KorToken.TYPE_SPECIAL, ";");
		}
		
		return mthnode;
	}
}

